package com.cn.esermis.netty;

import com.alibaba.fastjson2.JSONObject;
import com.cn.esermis.config.RedisQueueService;
import com.cn.esermis.constant.Constant;
import com.cn.esermis.model.BootNettyChannel;
import com.cn.esermis.server.PublisherService;
import com.cn.esermis.utils.HJ212MsgUtils;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Log4j2
@Component
@ChannelHandler.Sharable
public class BootNettyChannelInboundHandlerAdapter extends ChannelInboundHandlerAdapter {

    @Resource
    private RedisQueueService redisQueueService;
    @Resource
    private PublisherService publisherService;

    private static int MAX_CONNECTIONS = 0;

    public static BootNettyChannelInboundHandlerAdapter adapter;
    public BootNettyChannelInboundHandlerAdapter(){

    }
    private Map<String, String> dataMap = new HashMap<>();
    @PostConstruct
    public void init(){
        adapter = this;
        adapter.redisQueueService = this.redisQueueService;
        adapter.publisherService = this.publisherService;
        adapter.redisQueueService.setNumber(Constant.SERVER_ONLINE, MAX_CONNECTIONS);
    }


    /**
     * 注册时执行
     */
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        //注册通道
        super.channelRegistered(ctx);
        System.out.println("--channelRegistered--" + ctx.channel().id().toString());
    }

    /**
     * 离线时执行
     */
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        //通道离线
        super.channelUnregistered(ctx);
        System.out.println("--channelUnregistered--" + ctx.channel().id().toString());
    }

    /**
     * 从客户端收到新的数据时，这个方法会在收到消息时被调用
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        try {
            if (msg == null) {
                return;
            }
            InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
            String clientIp = inSocket.getAddress().getHostAddress();
            String message = msg.toString();
                log.info("IP:"+clientIp+"=============" + (new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date()) + "=============");
                log.info("收到数据：" + message);
            if (message.contains("&&")) {
                String[] split = message.split("&&");
                if (split.length == 3) {
                    JSONObject jsonObject = HJ212MsgUtils.dealMsg2(message);
                    if ((jsonObject!=null && jsonObject.getString(Constant.CN).equals(Constant.MINUTE))
                            ||(jsonObject!=null && jsonObject.getString(Constant.CN).equals(Constant.HOUR))){
                        if (ObjectUtils.notEqual(jsonObject.get("CN"),Constant.MINUTE_STR)){
                            String CN = jsonObject.getString("CN");
                            String MN = jsonObject.getString("MN");
                            String dataTime = jsonObject.getString("DataTime");
                            if (!adapter.redisQueueService.isElementExist(MN +":"+ CN +":"+ dataTime, dataTime)){
                                adapter.redisQueueService.addElementWithExpire(MN +":"+ CN +":"+ dataTime, dataTime, 3, TimeUnit.HOURS);
                                adapter.redisQueueService.sendMessage(Constant.SERVER, jsonObject);
                            }
                        }
                    }
                } else {
                    if (dataMap.get(ctx.channel().id().toString()) != null) {
                        String startStr = dataMap.get(ctx.channel().id().toString());
                        startStr = startStr + message;
                        JSONObject jsonObject = HJ212MsgUtils.dealMsg2(startStr);
                        if ((jsonObject!=null &&  jsonObject.getString(Constant.CN).equals(Constant.MINUTE))
                                || (jsonObject!=null && jsonObject.getString(Constant.CN).equals(Constant.HOUR))){
                            if (ObjectUtils.notEqual(jsonObject.get("CN"),Constant.MINUTE_STR)) {
                                String CN = jsonObject.getString("CN");
                                String MN = jsonObject.getString("MN");
                                String dataTime = jsonObject.getString("DataTime");
                                if (!adapter.redisQueueService.isElementExist(MN +":"+ CN +":"+ dataTime, dataTime)){
                                    adapter.redisQueueService.addElementWithExpire(MN +":"+ CN +":"+ dataTime, dataTime, 3, TimeUnit.HOURS);
                                    adapter.redisQueueService.sendMessage(Constant.SERVER, jsonObject);
                                }
                            }
                        }
                        dataMap.remove(ctx.channel().id().toString());
                    } else {
                        dataMap.put(ctx.channel().id().toString(), message);
                    }
                }
            }else{
                if (dataMap.get(ctx.channel().id().toString()) != null) {
                    String startStr = dataMap.get(ctx.channel().id().toString());
                    startStr = startStr + message;
                    dataMap.put(ctx.channel().id().toString(), startStr);
                }
            }
        } catch (Exception e) {
            System.out.println("channelRead--" + e.toString());
            throw  new RuntimeException(e);
        }
    }
    /**
     * 从客户端收到新的数据、读取完成时调用
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws IOException {
//        System.out.println("读取完成");
        ctx.flush();
    }

    /**
     * 当出现 Throwable 对象才会被调用，即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws IOException {
        log.error("========= 链接出现异常！exceptionCaught.");
        //打印错误内容
        cause.printStackTrace();
        //获取到这个通道
        BootNettyChannel bnc = BootNettyChannelCache.get("server:" + ctx.channel().id().toString());
        if (bnc != null) {
            //移除掉
            BootNettyChannelCache.remove("server:" + ctx.channel().id().toString());
        }
        ctx.close();//抛出异常，断开与客户端的连接
    }

    /**
     * 客户端与服务端第一次建立连接时 执行
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception, IOException {
        //调用原始方法
        super.channelActive(ctx);
        ctx.channel().read();
        //获取到IP地址
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = inSocket.getAddress().getHostAddress();
        //此处不能使用ctx.close()，否则客户端始终无法与服务端建立连接
        System.out.println("channelActive:" + clientIp + ctx.name() + "HOLLER");
        ctx.writeAndFlush(Unpooled.copiedBuffer(clientIp + "：" + ctx.name() + "：加入链接", CharsetUtil.UTF_8));
        adapter.redisQueueService.setNumber(Constant.SERVER_ONLINE, adapter.redisQueueService.getNumber(Constant.SERVER_ONLINE)+1);
    }

    /**
     * 客户端与服务端 断连时 执行
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception, IOException {
        super.channelInactive(ctx);
        //获取到IP
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = inSocket.getAddress().getHostAddress();
        System.out.println("channelInactive:" + clientIp);

        //获取到通道
        BootNettyChannel bnc = BootNettyChannelCache.get("server:" + ctx.channel().id().toString());
        if (bnc != null) {
            //移除
            BootNettyChannelCache.remove("server:" + ctx.channel().id().toString());
        }
        ctx.close(); //断开连接时，必须关闭，否则造成资源浪费，并发量很大情况下可能造成宕机
        adapter.redisQueueService.setNumber(Constant.SERVER_ONLINE,
                adapter.redisQueueService.getNumber(Constant.SERVER_ONLINE)!=null?adapter.redisQueueService.getNumber(Constant.SERVER_ONLINE)-1:0);
    }

    /**
     * 服务端当read超时, 会调用这个方法
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception, IOException {
        if (evt instanceof IdleStateEvent) {
//            IdleStateEvent event = (IdleStateEvent) evt;
//
//            String eventType = null;
//            switch (event.state()) {
//                case READER_IDLE:
//                    eventType = "读超时";
//                    break;
//                case WRITER_IDLE:
//                    eventType = "写超时";
//                    break;
//                case ALL_IDLE:
//                    eventType = "读写超时";
//                    break;
//                default:
//                    eventType = "设备超时";
//            }
//            log.warn(ctx.channel().id() + " : " + eventType + "---> 关闭该设备");
            dataMap.remove(ctx.channel().id().toString());
            ctx.channel().close();
        }
    }
}
